use thiserror::Error;
use std::fmt;

/// Error codes for different types of system errors
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum ErrorCode {
    ParseError,
    GraphUpdateError,
    StorageError,
    LanguageNotSupported,
    IncrementalUpdateFailed,
    InvalidInput,
    NetworkError,
    ConfigurationError,
    ResourceExhausted,
    InternalError,
}

impl fmt::Display for ErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ErrorCode::ParseError => write!(f, "PARSE_ERROR"),
            ErrorCode::GraphUpdateError => write!(f, "GRAPH_UPDATE_ERROR"),
            ErrorCode::StorageError => write!(f, "STORAGE_ERROR"),
            ErrorCode::LanguageNotSupported => write!(f, "LANGUAGE_NOT_SUPPORTED"),
            ErrorCode::IncrementalUpdateFailed => write!(f, "INCREMENTAL_UPDATE_FAILED"),
            ErrorCode::InvalidInput => write!(f, "INVALID_INPUT"),
            ErrorCode::NetworkError => write!(f, "NETWORK_ERROR"),
            ErrorCode::ConfigurationError => write!(f, "CONFIGURATION_ERROR"),
            ErrorCode::ResourceExhausted => write!(f, "RESOURCE_EXHAUSTED"),
            ErrorCode::InternalError => write!(f, "INTERNAL_ERROR"),
        }
    }
}

/// Main error type for the CodeGraph system
#[derive(Error, Debug)]
pub enum CodeGraphError {
    #[error("Parse error: {message}")]
    Parse {
        code: ErrorCode,
        message: String,
        context: Option<serde_json::Value>,
    },

    #[error("Graph update error: {message}")]
    GraphUpdate {
        code: ErrorCode,
        message: String,
        context: Option<serde_json::Value>,
    },

    #[error("Storage error: {message}")]
    Storage {
        code: ErrorCode,
        message: String,
        context: Option<serde_json::Value>,
    },

    #[error("Language not supported: {language}")]
    LanguageNotSupported {
        language: String,
        supported_languages: Vec<String>,
    },

    #[error("Incremental update failed: {message}")]
    IncrementalUpdateFailed {
        message: String,
        context: Option<serde_json::Value>,
    },

    #[error("Invalid input: {message}")]
    InvalidInput {
        message: String,
        field: Option<String>,
    },

    #[error("Network error: {message}")]
    Network {
        message: String,
        status_code: Option<u16>,
    },

    #[error("Configuration error: {message}")]
    Configuration {
        message: String,
        config_path: Option<String>,
    },

    #[error("Resource exhausted: {resource}")]
    ResourceExhausted {
        resource: String,
        limit: Option<u64>,
        current: Option<u64>,
    },

    #[error("Internal error: {message}")]
    Internal {
        message: String,
        source: Option<Box<dyn std::error::Error + Send + Sync>>,
    },
}

impl CodeGraphError {
    pub fn code(&self) -> ErrorCode {
        match self {
            CodeGraphError::Parse { code, .. } => code.clone(),
            CodeGraphError::GraphUpdate { code, .. } => code.clone(),
            CodeGraphError::Storage { code, .. } => code.clone(),
            CodeGraphError::LanguageNotSupported { .. } => ErrorCode::LanguageNotSupported,
            CodeGraphError::IncrementalUpdateFailed { .. } => ErrorCode::IncrementalUpdateFailed,
            CodeGraphError::InvalidInput { .. } => ErrorCode::InvalidInput,
            CodeGraphError::Network { .. } => ErrorCode::NetworkError,
            CodeGraphError::Configuration { .. } => ErrorCode::ConfigurationError,
            CodeGraphError::ResourceExhausted { .. } => ErrorCode::ResourceExhausted,
            CodeGraphError::Internal { .. } => ErrorCode::InternalError,
        }
    }

    pub fn parse_error(message: impl Into<String>) -> Self {
        Self::Parse {
            code: ErrorCode::ParseError,
            message: message.into(),
            context: None,
        }
    }

    pub fn graph_update_error(message: impl Into<String>) -> Self {
        Self::GraphUpdate {
            code: ErrorCode::GraphUpdateError,
            message: message.into(),
            context: None,
        }
    }

    pub fn storage_error(message: impl Into<String>) -> Self {
        Self::Storage {
            code: ErrorCode::StorageError,
            message: message.into(),
            context: None,
        }
    }

    pub fn language_not_supported(language: impl Into<String>, supported: Vec<String>) -> Self {
        Self::LanguageNotSupported {
            language: language.into(),
            supported_languages: supported,
        }
    }

    pub fn incremental_update_failed(message: impl Into<String>) -> Self {
        Self::IncrementalUpdateFailed {
            message: message.into(),
            context: None,
        }
    }

    pub fn invalid_input(message: impl Into<String>) -> Self {
        Self::InvalidInput {
            message: message.into(),
            field: None,
        }
    }

    pub fn internal_error(message: impl Into<String>) -> Self {
        Self::Internal {
            message: message.into(),
            source: None,
        }
    }

    pub fn config_error(message: impl Into<String>) -> Self {
        Self::Configuration {
            message: message.into(),
            config_path: None,
        }
    }
}

/// Result type alias for CodeGraph operations
pub type Result<T> = std::result::Result<T, CodeGraphError>;

/// Context for error recovery and debugging
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ErrorContext {
    pub operation: String,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub metadata: serde_json::Value,
}

impl ErrorContext {
    pub fn new(operation: impl Into<String>) -> Self {
        Self {
            operation: operation.into(),
            timestamp: chrono::Utc::now(),
            metadata: serde_json::Value::Null,
        }
    }

    pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
        self.metadata = metadata;
        self
    }
}

impl From<serde_json::Error> for CodeGraphError {
    fn from(err: serde_json::Error) -> Self {
        Self::Storage {
            code: ErrorCode::StorageError,
            message: format!("JSON serialization error: {}", err),
            context: None,
        }
    }
}

impl From<std::fmt::Error> for CodeGraphError {
    fn from(err: std::fmt::Error) -> Self {
        Self::Storage {
            code: ErrorCode::StorageError,
            message: format!("String formatting error: {}", err),
            context: None,
        }
    }
}

impl From<semver::Error> for CodeGraphError {
    fn from(err: semver::Error) -> Self {
        Self::Configuration {
            message: format!("Version parsing error: {}", err),
            config_path: None,
        }
    }
}

impl From<std::io::Error> for CodeGraphError {
    fn from(err: std::io::Error) -> Self {
        Self::Storage {
            code: ErrorCode::StorageError,
            message: format!("IO error: {}", err),
            context: None,
        }
    }
}
